#include "HashFile.h"



HashFile::HashFile(){ }


bool HashFile::abrir(std::string nomArch){
	if (this->_arch.is_open()){
		return false;
	}
	return this->_arch.abrirArch(this->_headerDispersion, nomArch);
}


bool HashFile::crear(std::string nomArch){
	if (this->_arch.is_open()){
		return false;
	}

	// Se inicializa la cabecera con un bloque vacio
	this->_headerDispersion.setCantBloques(2);
	bool exito = this->_arch.crearArch(this->_headerDispersion, nomArch);
	if (!exito) return false;

	// Guarda un bloque de datos vacio
	BloqueHash b;
	int pos = this->_arch.guardar(b);

	if (pos == -1) return false;
	else  return true;
}


Resultado HashFile::insertarRegistro(RegistroHash reg){
	unsigned int posBloque = this->_headerDispersion.getPosBloque(reg);

	BloqueHash b;
	cargarBloque(b, posBloque);

	Resultado res = b.agregarRegistro(reg);
	if (res == OVERFLOW){
		resolverOverflow(b, reg);
		res = OK;
	}

	guardarBloque(b, posBloque);

	if (res == MODIFICADO){
		return OK;
	} else {
		return res;
	}
}


Resultado HashFile::eliminarRegistro(const RegistroHash& reg){
	unsigned int posBloque = this->_headerDispersion.getPosBloque(reg);

	BloqueHash b;
	cargarBloque(b, posBloque);

	Resultado res = b.quitarRegistroPorId(reg);
	guardarBloque(b, posBloque);

	return res;
}


RegistroHash HashFile::buscarRegistro(const RegistroHash& reg){

	unsigned int posBloque = this->_headerDispersion.getPosBloque(reg);

	BloqueHash b;
	cargarBloque(b, posBloque);

	RegistroHash rEncontrado;
	b.buscarRegistroPorId(reg, rEncontrado);

	// Si no lo encuentra devuelve uno vacio
	return rEncontrado;
}


void HashFile::resolverOverflow(BloqueHash &bloque, RegistroHash reg){
	// Un nuevo bloque con el que se redispersan los registros
	BloqueHash bNuevo;
	unsigned int posBloqueNuevo = this->_headerDispersion.getPosBloqueLibre();

	// Se indica a la tabla de dispersion la sitiacion de sobreflujo
	this->_headerDispersion.overflowBloque(reg, bloque, posBloqueNuevo);

	// Se duplican los tamaños de dispersion de los bloques
	unsigned int newTam = bloque.getTamDispersion() *2;
	bloque.setTamDispersion(newTam);
	bNuevo.setTamDispersion(newTam);

	redispersarRegistros(bloque, bNuevo, posBloqueNuevo);

	// Uno de los dos bloques pudo haber terminado con overflow
	if (bloque.getEstado() == OVERFLOW){
		resolverOverflow(bloque, reg);

	} else if (bNuevo.getEstado() == OVERFLOW){
		resolverOverflow(bNuevo, reg);
	}

	// Ya esta resuelto el overflow
	guardarBloque(bNuevo, posBloqueNuevo);
}


// Realiza la redispersion de los registros
void HashFile::redispersarRegistros(BloqueHash& bOver, BloqueHash& bEmpty, unsigned int posBEmpty) const{

	// Redispiesa el contenido del bloque lleno entre el vacio y el temporal
	BloqueHash bTemp;
	for (int i = 0; i < bOver.getCantElementos(); ++i){

		RegistroHash rTemp = bOver.getRegAt(i);
		unsigned int posBloque = this->_headerDispersion.getPosBloque(rTemp);

		// Solo hay posibilidades de dispersión
		if (posBloque == posBEmpty){
			bEmpty.agregarRegistro(rTemp);
		} else {
			bTemp.agregarRegistro(rTemp);
		}
	}

	// Intercambian sus contenidos el bloque lleno y el temporal
	bOver.swap(bTemp);
}



unsigned int HashFile::guardarBloque(BloqueHash& b, int pos){
	if (pos == -1){
		// Nuevo bloque, aumento la cantidad de bloques que hay en el archivo
//		this->_headerDispersion.setCantBloques(this->_headerDispersion.getCantBloques()+1);
	}

	return this->_arch.guardar(b, pos);
}


void HashFile::cargarBloque(BloqueHash& b, int pos){
	this->_arch.leer(b, pos);
}

